﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Web.Services.Protocols;

namespace SPWebShell.cmdlet
{
    [Cmdlet(VerbsCommon.Add, "SPFile")]
    public class UploadFile : CmdLetBase
    {
        [Parameter(Mandatory = false, HelpMessage = "The title of the List")]
        [ValidateNotNullOrEmpty]
        public string List { get; set; }

        [Parameter(Mandatory = true, HelpMessage = "The absolute URL to the web site where the List is located or any item/view within the List. e.g. 'http://server/sites/SiteA/doclib1/Allitems.aspx'")]
        [ValidateNotNullOrEmpty]
        public string Url { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "The path of the file which will be uploaded to SharePoint. E.g. '.\\hello.docx' or 'c:\\temp\\hello.docx'")]
        [ValidateNotNullOrEmpty]
        public string File { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "The folder path relative to the SharePoint document library where the file will be copied. e.g. '/folder1/' or '/parent/subfolder1/'" )]
        [ValidateNotNullOrEmpty]
        [AllowEmptyString]
        public string Folder { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "The name of the destination file on SharePoint. If not specified then the file name is resolved from the File property")]
        public string Name { get; set; }

        [Parameter(Mandatory = false, HelpMessage = "A comma separated list of internal names whose values will be applied to the document list item")]
        public string[] Properties { get; set; }
        [Parameter(Mandatory = false, HelpMessage = "A comma separated list of field values specified in the same order as Properties")]
        public string[] Values { get; set; }

        protected override void ProcessRecord()
        {
            try
            {
                WriteVerbose("Start time: " + DateTime.Now.ToString());
                string filePath=System.IO.Path.GetFullPath(this.File);
                if (!System.IO.File.Exists(filePath))
                {
                    throw new Exception(string.Format("The file:{0} was not found",filePath));
                }
                ISPRepository repos = base.SPRepos;
                SPDoclibLocation location = repos.ResolveDoclibUrlFromPageUrl(this.Url);
                //If a List name has been provided then use this name
                if (!string.IsNullOrEmpty(this.List)) location.DoclibName = this.List;
                entity.SPList listInfo = GetListInfoFromCache(location);
                PurifyFieldNames(listInfo);
                byte[] content = System.IO.File.ReadAllBytes(filePath);
                string fileNameOnServer=this.Name;
                if (string.IsNullOrEmpty(fileNameOnServer))
                {
                    fileNameOnServer=System.IO.Path.GetFileName(this.File);
                }
                entity.SPListItem item=SPRepos.UploadFile(content, listInfo,location,fileNameOnServer ,this.Folder, this.Properties, this.Values);
                WriteObject(item);
                base.ProcessRecord();
            }
            catch (SoapException ex)
            {
                string message = ex.Detail.OuterXml;
                throw new Exception(message, ex);
            }
            finally
            {
                WriteVerbose("End time: " + DateTime.Now.ToString());
            }
        }
        /// <summary>
        /// Iterates through all meta-data propery names and trims/converts to right case.
        /// Rationale - to make it easy for user to specify internal names without being too strict
        /// </summary>
        private void PurifyFieldNames(entity.SPList listInfo)
        {
            if (this.Properties == null) return;
            if (this.Properties.Length == 0) return;
            List<string> purifiedpropnames = new List<string>();
            foreach (string propname in this.Properties)
            {
                if (string.IsNullOrEmpty(propname)) throw new Exception("Cannot accept NULL property names");
                string propnametrimmed=propname.Trim();
                entity.SPField thisField=listInfo.Fields.FirstOrDefault(f=>(f.InternalName.Equals(propnametrimmed, StringComparison.CurrentCultureIgnoreCase)));
                if (thisField == null) throw new Exception(string.Format("No property found with the name:{0}", propnametrimmed));
                purifiedpropnames.Add(thisField.InternalName);
            }
            this.Properties = purifiedpropnames.ToArray();
        }
        /// <summary>
        /// Retrieves a caches copy of the List meta-data
        /// </summary>
        /// <param name="listlocation"></param>
        /// <returns></returns>
        entity.SPList GetListInfoFromCache(SPDoclibLocation listlocation)
        {
            System.Runtime.Caching.MemoryCache cache = System.Runtime.Caching.MemoryCache.Default;
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            string urlpluslistname=listlocation.DoclibName+listlocation.SiteUrl;
            byte[] input=System.Text.Encoding.UTF8.GetBytes(urlpluslistname);
            byte[] bKey = md5.ComputeHash(input);
            string sKey = System.Text.Encoding.UTF8.GetString(bKey);
            entity.SPList listInfo =null;
            if (!cache.Contains(sKey))
            {
                VerboseLogger("List metadata was not found in cache");
                listInfo = base.SPRepos.GetList(listlocation);
                System.Runtime.Caching.CacheItemPolicy pol = new System.Runtime.Caching.CacheItemPolicy();
                pol.SlidingExpiration = new TimeSpan(0, 1, 0);
                pol.Priority = System.Runtime.Caching.CacheItemPriority.Default;
                cache.Add(sKey, listInfo, pol);
            }
            else
            {
                VerboseLogger("Found list metadata in cache");
                listInfo =(entity.SPList) cache[sKey];
            }
            return listInfo;
        }
    }
}
